package com.terry.trace.test.controller;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.ttl.threadpool.TtlExecutors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.*;

/**
 * 线程创建类
 * @author dazhuang
 * @date 2023/12/13
 */
@Component
public class ThreadPoolConfig {
    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);
    //获取cpu核心数
    private final static int processNum = Runtime.getRuntime().availableProcessors();

    //自定义使用参数
    @Value("${async.executor.thread.core_pool_size:2}")
    private int corePoolSize;   //配置核心线程数
    @Value("${async.executor.thread.max_pool_size:36}")
    private int maxPoolSize;    //配置最大线程数
    @Value("${async.executor.thread.queue_capacity:36}")
    private int queueCapacity;
    @Value("${async.executor.thread.keep_alive_seconds:60}")
    private int keepAliveSeconds; //设置线程空闲等待时间 s

    private final static Map<String, ThreadPoolTaskExecutor> threadPoolTaskExecutorConcurrentHashMap
            = new ConcurrentHashMap<String, ThreadPoolTaskExecutor>();

    public ThreadPoolTaskExecutor getThreadPoolExecutor(String namePrefix){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = threadPoolTaskExecutorConcurrentHashMap.get(namePrefix);
        if(threadPoolTaskExecutor == null){
            synchronized (namePrefix.intern()){
                threadPoolTaskExecutor = threadPoolTaskExecutorConcurrentHashMap.get(namePrefix);
                if(threadPoolTaskExecutor == null){
                    threadPoolTaskExecutor = asyncServiceExecutor(namePrefix, corePoolSize, maxPoolSize, queueCapacity, keepAliveSeconds);
                    threadPoolTaskExecutorConcurrentHashMap.put(namePrefix, threadPoolTaskExecutor);
                }
            }
        }
        return threadPoolTaskExecutor;
    }

    /**
     * @param namePrefix 线程池中的线程的名称前缀
     * @param corePoolSize 核心线程数
     * @param maxPoolSize 最大线程数
     * @param queueCapacity 配置队列大小
     * @param keepAliveSeconds 线程空闲等待时间 s
     * @return {@link ThreadPoolTaskExecutor}
     */
    public ThreadPoolTaskExecutor getThreadPoolExecutor(String namePrefix, int corePoolSize, int maxPoolSize, int queueCapacity, int keepAliveSeconds){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = threadPoolTaskExecutorConcurrentHashMap.get(namePrefix);
        if(threadPoolTaskExecutor == null){
            synchronized (namePrefix.intern()){
                threadPoolTaskExecutor = threadPoolTaskExecutorConcurrentHashMap.get(namePrefix);
                if(threadPoolTaskExecutor == null){
                    threadPoolTaskExecutor = asyncServiceExecutor(namePrefix, corePoolSize, maxPoolSize, queueCapacity, keepAliveSeconds);
                    threadPoolTaskExecutorConcurrentHashMap.put(namePrefix, threadPoolTaskExecutor);
                }
            }
        }
        return threadPoolTaskExecutor;
    }

    //1、自定义asyncServiceExecutor线程池

    /**
     * @param namePrefix
     * @param corePoolSize
     * @param maxPoolSize
     * @param queueCapacity
     * @param keepAliveSeconds
     * @return {@link ThreadPoolTaskExecutor}
     */
    private ThreadPoolTaskExecutor asyncServiceExecutor(String namePrefix, int corePoolSize, int maxPoolSize, int queueCapacity, int keepAliveSeconds) {
        logger.info("start asyncServiceExecutor......");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        //线程池创建的核心线程数，线程池维护线程的最少数量，即使没有任务需要执行，也会一直存活
        executor.setCorePoolSize(corePoolSize);
        //配置队列大小 设置任务等待队列的大小
        //阻塞队列 当核心线程数达到最大时，新任务会放在队列中排队等待执行
        executor.setQueueCapacity(queueCapacity);
        //配置最大线程数
        //最大线程池数量，当线程数>=corePoolSize，且任务队列已满时。线程池会创建新线程来处理任务
        //任务队列已满时, 且当线程数=maxPoolSize，，线程池会拒绝处理任务而抛出异常
        executor.setMaxPoolSize(maxPoolSize);
        //设置线程空闲等待时间 s
        //当线程空闲时间达到keepAliveTime时，线程会退出，直到线程数量=corePoolSize
        //允许线程空闲时间30秒，当maxPoolSize的线程在空闲时间到达的时候销毁
        //如果allowCoreThreadTimeout=true，则会直到线程数量=0
        executor.setKeepAliveSeconds(keepAliveSeconds);
        //配置线程池中的线程的名称前缀
        //设置线程池内线程名称的前缀-------阿里编码规约推荐--方便出错后进行调试
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy：拒绝策略：当线程数已经达到maxSize的时候，如何处理新任务
        // CallerRunsPolicy()：交由调用方线程运行，比如 main 线程；如果添加到线程池失败，那么主线程会自己去执行该任务，不会等待线程池中的线程去执行, (个人推荐)
        // AbortPolicy()：该策略是线程池的默认策略，如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
        // DiscardPolicy()：如果线程池队列满了，会直接丢掉这个任务并且不会有任何异常
        // DiscardOldestPolicy()：丢弃队列中最老的任务，队列满了，会将最早进入队列的任务删掉腾出空间，再尝试加入队列
        // CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean，这样这些异步任务的销毁就会先于Redis线程池的销毁
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //设置线程池中任务的等待时间，如果超过这个时候还没有销毁就强制销毁，以确保应用最后能够被关闭，而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);
        //执行初始化
        executor.initialize();
        final Executor ttlExecutor = TtlExecutors.getTtlExecutor(executor);
        return executor;
    }

    public static void main(String[] args) {
        ThreadPoolConfig config = new ThreadPoolConfig();
        final ThreadPoolTaskExecutor aaa = config.asyncServiceExecutor("aaa", 2, 5, 10, 60);
        final CountDownLatch latch = new CountDownLatch(100);
        for (int i = 0; i < 100; i++) {
            Integer a = i;
            aaa.execute(new Runnable() {

                /**
                 * When an object implementing interface <code>Runnable</code> is used
                 * to create a thread, starting the thread causes the object's
                 * <code>run</code> method to be called in that separately executing
                 * thread.
                 * <p>
                 * The general contract of the method <code>run</code> is that it may
                 * take any action whatsoever.
                 *
                 * @see Thread#run()
                 */
                @Override
                public void run() {
                    try {
                        TimeUnit.MILLISECONDS.sleep(RandomUtil.randomInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //System.out.println(Thread.currentThread().getName() + "=====" + a);
                    latch.countDown();
                }
            });
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    }
}